/*************************************************************************** 
 * (c) 2006 Dmitry Osinovsky, osdmsoftware@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License as        *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 ***************************************************************************/
/***************************************************************************
 * Portions Copyright (C) Rick Brewster, Chris Crosetto, Dennis Dietrich,
 *                        Tom Jackson, Michael Kelsey, Brandon Ortiz,
 *                        Craig Taylor, Chris Trevino, and Luke Walker
 * Portions Copyright (C) Microsoft Corporation. All Rights Reserved.
 * See LicenseMIT.txt for complete licensing and attribution information.
 **************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Resources;
using OsdmSoftware.CommonLibrary;

namespace OsdmSoftware.PocketPicture
{
    public abstract class BaseBrushTool: PaintTool
    {
        protected Point lastMouseXY;

        /* Three fields to describe segment border that is perpendicular to axis (axis is connecting mouse movements) */
        private double lastPerpFactor; // x(y) = lastMouseXY.X + (y - lastMouseXY.Y) * lastPerpFactor
        private bool isLastPerpFactorInfinite; // factor is infinite is border is horizontal
        private bool outerForLastPerpIsOnTheLeft; // for horizontal border left == up, right == down

        // For first segment we need to draw opening hemisphere
        private bool isFirst;

        // Target bitmap
        private BitmapData bmpData;

        // Source bitmap (that is, bitmap without current brush drawing)
        protected BitmapData sourceBmpData;

        protected int bitmapWidth;
        protected int bitmapHeight;
        private int oldBresenhamY;

        protected int currX, currY; // coordinates of current mouse movement
        protected double l; // segment length
        protected double dx; // lastMouseXY.X - currX
        protected double dy; // lastMouseXY.Y - currY
        protected int intL100; // segment length
        protected int intDX; // lastMouseXY.X - currX
        protected int intDY; // lastMouseXY.Y - currY
        protected ColorBgra selectedColor; // cached selected color
        protected byte[,] coefs; // array of bytes equal to bitmap in size, you can use it for storing transparency info 

        /* Class describing circle sector drawing info*/
        private class HemisphereInfo
        {
            public bool drawHemisphere;
            public bool isLeftSideUnlimited;
            public bool isRightSideUnlimited;
            public double leftSideLineFactor;
            public double rightSideLineFactor;
        }

        /* 
         * Low hemisphere means hemisphere with lesser y. On the screen it is high hemisphere.
         * Vice versa for high hemisphere.
         */
        private HemisphereInfo lowHemisphere = new HemisphereInfo(); 
        private HemisphereInfo highHemisphere = new HemisphereInfo();

        public BaseBrushTool(string hint) : base(hint) { }

        public override void SelectTool()
        {
            Form.HintLabel.Text = "Painting with brush";
        }

        public override void ClearStatus()
        {
            coefs = null;
            base.ClearStatus();
        }

        public override void MouseDown(int x, int y)
        {
            lastMouseXY.X = x;
            lastMouseXY.Y = y;
            isFirst = true;
            if (coefs == null)
                coefs = new byte[Form.BitmapZoomer.Bitmap.Width, Form.BitmapZoomer.Bitmap.Height];
            //DrawSegment(x + 1, y, false);
        }

        protected virtual void ComputeAdditionalData()
        {
        }

        protected void DrawSegment(int x, int y, bool useLastNorm)
        {
            // TODO: Bresenham-ise to get rid of floating point arithmetic

            bool isPerpFactorInfinite;
            bool outerForNextPerpIsOnTheLeft;
            bool outerForCurrPerpIsOnTheLeft;
            double xfactor;

            Bitmap bmp = Form.BitmapZoomer.Bitmap;
            Rectangle rect1 = new Rectangle(0, 0, bmp.Width, bmp.Height);
            bmpData = bmp.LockBits(rect1, ImageLockMode.ReadWrite, ColorBgra.PixelFormat);
            sourceBmpData = Form.LastModifications.LockBits(rect1, ImageLockMode.ReadWrite, ColorBgra.PixelFormat);
            bitmapHeight = bmp.Height;
            bitmapWidth = bmp.Width;
            currX = x;
            currY = y;
            selectedColor = ColorBgra.FromColor(Form.PaletteSelectedColor);
            //sourceBmpData = bmpData;

            if (x == lastMouseXY.X && y == lastMouseXY.Y)
            {
                xfactor = lastPerpFactor;
                isPerpFactorInfinite = isLastPerpFactorInfinite;
                outerForCurrPerpIsOnTheLeft = outerForLastPerpIsOnTheLeft;
                outerForNextPerpIsOnTheLeft = outerForLastPerpIsOnTheLeft;
                l = 0;
                intL100 = 0;
                dx = 0;
                intDX = 0;
                dy = 0;
                intDY = 0;
                ComputeAdditionalData();
            }
            else
            {
                /*
                 * Comments to formulae:
                 * We are drawing a rectangle with axis on a line lastMouseXY -> (x,y) and width equal to Form.PenWidth
                 * Lets take triangle which is formed by axis and some point on the rectangle border line that is parallel to axis (RBP)
                 * 
                 *                        (x, y) 
                 *                   *   /
                 *                  *   @
                 *                 *   *   *
                 *        RBP(1)--@   s   @--RBP(2)
                 *               *   i   *
                 *              *   x   *
                 *             *   a   *
                 *            *   *   *
                 *               @   *
                 *             /    *
                 * lastMouseXY
                 * 
                 * Triangle area (S) = base * altitude / 2
                 * in our case base = l, altitude = Form.PenWidth / 2, so
                 * S = l * Form.PenWidth / 4
                 * 
                 * On the other hand, an area of the triangle formed by two vectors (x1, y1) - (x2, y2) is equal to
                 * abs(x2*y1)/2 - abs(x1*y2)/2
                 * 
                 * Lets take RBP as point of origin, then x1 = (x - RBP.X), y1 = (y - RBP.Y), x2 = (lastMouseXY.X - RBP.X), y2 = (lastMouseXY.Y - RBP.Y)
                 * So we have an equation:
                 * l * Form.PenWidth / 4 = abs((lastMouseXY.X - RBP.X)*(y - RBP.Y))/2 - abs((x - RBP.X)*(lastMouseXY.Y - RBP.Y))/2
                 * 
                 * from which we can deduce that
                 * 
                 * RBP.X = ((+ or -)Form.PenWidth / 2 * l  - x * lastMouseXY.Y + lastMouseXY.X * y - RBP.Y * (lastMouseXY.X - x)) / (y - lastMouseXY.Y)
                 * 
                 * Now if we take any Y coordinate we can calculate two RBP of rectangle
                 * 
                 * But now lets not forget about border lines that are perpendicular to axis.
                 * Fortunately, we can just use line equation for them: x = add + factor * y
                 * factor for axis is (lastMouseXY.X - x) / (lastMouseXY.Y - x)
                 * factor for perpendicular line is equal to 1/(factor for axis), that is (lastMouseXY.Y - x)/(lastMouseXY.X - x)
                 * Knowing that border line must pass (x, y) or lastMouseXY it is easy to compute "add" component
                 * 
                 */

                // Segment length
                l = Math.Sqrt((double) (y - lastMouseXY.Y) * (y - lastMouseXY.Y) + (double) (x - lastMouseXY.X) * (x - lastMouseXY.X));
                intL100 = (int) (l * 100 + 0.5);
                dx = lastMouseXY.X - x;
                intDX = lastMouseXY.X - x;
                dy = lastMouseXY.Y - y;
                intDY = lastMouseXY.Y - y;
                double pt1 = 0, pt2 = 0;

                if (lastMouseXY.Y != y)
                {
                    pt1 = Form.PenWidth * l / 2D - x * lastMouseXY.Y + lastMouseXY.X * y;
                    pt2 = -Form.PenWidth * l / 2D - x * lastMouseXY.Y + lastMouseXY.X * y;
                    if (-pt1 / dy > -pt2 / dy)
                    {
                        double tmp;
                        tmp = pt1;
                        pt1 = pt2;
                        pt2 = tmp;
                    }
                }

                double xminadd, xmaxadd;

                int y1, y2;

                if (lastMouseXY.X == x)
                {
                    isPerpFactorInfinite = true;

                    if (y < lastMouseXY.Y)
                    {
                        y1 = y;
                        y2 = lastMouseXY.Y;

                        outerForNextPerpIsOnTheLeft = true; // on the up
                        outerForCurrPerpIsOnTheLeft = false; // on the bottom
                    }
                    else
                    {
                        y1 = lastMouseXY.Y;
                        y2 = y;

                        outerForNextPerpIsOnTheLeft = false; // on the bottom
                        outerForCurrPerpIsOnTheLeft = true; // on the top
                    }

                    xfactor = 0;
                    xminadd = 0;
                    xmaxadd = bmp.Width;
                }
                else
                {
                    isPerpFactorInfinite = false;

                    if (y < lastMouseXY.Y)
                    {
                        y1 = y - Form.PenWidth / 2;
                        y2 = lastMouseXY.Y + Form.PenWidth / 2;
                    }
                    else
                    {
                        y1 = lastMouseXY.Y - Form.PenWidth / 2;
                        y2 = y + Form.PenWidth / 2;
                    }

                    xfactor = dy / dx;
                    
                    if (lastMouseXY.X > x)
                    {
                        xminadd = x + y * xfactor;
                        xmaxadd = lastMouseXY.X + lastMouseXY.Y * xfactor;

                        outerForNextPerpIsOnTheLeft = true;
                        outerForCurrPerpIsOnTheLeft = false;
                    }
                    else
                    {
                        xmaxadd = x + y * xfactor;
                        xminadd = lastMouseXY.X + lastMouseXY.Y * xfactor;
                        outerForNextPerpIsOnTheLeft = false;
                        outerForCurrPerpIsOnTheLeft = true;
                    }

                    xfactor = -xfactor;
                }

                if (y1 < 0)
                    y1 = 0;

                if (y2 >= bmp.Height)
                    y2 = bmp.Height - 1;

                ComputeAdditionalData();

                unsafe
                {
                    byte* pSourceRow = (byte*) sourceBmpData.Scan0.ToPointer();
                    byte* pTargetRow = (byte*) bmpData.Scan0.ToPointer();
                    pSourceRow += sourceBmpData.Stride * y1;
                    pTargetRow += bmpData.Stride * y1;

                    for (int yn = y1; yn <= y2; yn++)
                    {
                        int xs, xe;
                        if (lastMouseXY.Y != y)
                        {
                            xs = (int) Math.Ceiling((pt1 - yn * dx) / -dy);
                            xe = (int) Math.Floor((pt2 - yn * dx) / -dy);
                            /*
                            int x1 = (int) ((pt1 - yn * dx) / -dy);
                            int x2 = (int) ((pt2 - yn * dx) / -dy);
                            if (x1 < x2)
                            {
                            }
                            else
                            {
                                xs = x2;
                                xe = x1;
                            }*/
                        }
                        else
                        {
                            xs = 0;
                            xe = bmp.Width - 1;
                        }

                        xs = Math.Max(xs, (int) Math.Ceiling(xminadd + xfactor * yn));
                        xe = Math.Min(xe, (int) Math.Floor(xmaxadd + xfactor * yn));

                        if (xs < 0)
                            xs = 0;
                        else if (xs >= Form.BitmapZoomer.Bitmap.Width)
                            xs = Form.BitmapZoomer.Bitmap.Width - 1;
                        if (xe < 0)
                            xe = 0;
                        else if (xe >= Form.BitmapZoomer.Bitmap.Width)
                            xe = Form.BitmapZoomer.Bitmap.Width - 1;

                        DrawLine(pSourceRow, pTargetRow, xs, xe, yn, false);

                        pSourceRow += sourceBmpData.Stride;
                        pTargetRow += bmpData.Stride;
                    }
                }
            }

            /*
             * Drawing circle sector connecting this and last segment.
             * Good luck for anyone trying to understand how it works, cause I don't :-)
             * I know that it is ugly. Well, try to make it better - that is what open source is about.
             */

            if (x == lastMouseXY.X && y == lastMouseXY.Y && isFirst)
            {
                lowHemisphere.drawHemisphere = true;
                lowHemisphere.isLeftSideUnlimited = true;
                lowHemisphere.isRightSideUnlimited = true;

                highHemisphere.drawHemisphere = true;
                highHemisphere.isLeftSideUnlimited = true;
                highHemisphere.isRightSideUnlimited = true;

                isFirst = false;
            }
            else
            {
                if (isFirst)
                {
                    lastPerpFactor = xfactor;
                    outerForLastPerpIsOnTheLeft = outerForCurrPerpIsOnTheLeft;
                    isLastPerpFactorInfinite = isPerpFactorInfinite;
                    isFirst = false;
                }

                if (outerForCurrPerpIsOnTheLeft == outerForLastPerpIsOnTheLeft)
                {
                    if (!outerForCurrPerpIsOnTheLeft)
                    {
                        if (CompareFactors(xfactor, lastPerpFactor, isPerpFactorInfinite, isLastPerpFactorInfinite) < 0 || (isPerpFactorInfinite && isLastPerpFactorInfinite))
                        {
                            lowHemisphere.drawHemisphere = !isPerpFactorInfinite;
                            lowHemisphere.isLeftSideUnlimited = false;
                            lowHemisphere.isRightSideUnlimited = true;
                            lowHemisphere.leftSideLineFactor = xfactor;
                            lowHemisphere.rightSideLineFactor = 0;

                            highHemisphere.drawHemisphere = true;
                            highHemisphere.isLeftSideUnlimited = isLastPerpFactorInfinite;
                            highHemisphere.isRightSideUnlimited = true;
                            highHemisphere.leftSideLineFactor = lastPerpFactor;
                            highHemisphere.rightSideLineFactor = 0;
                        }
                        else
                        {
                            lowHemisphere.drawHemisphere = !isLastPerpFactorInfinite;
                            lowHemisphere.isLeftSideUnlimited = false;
                            lowHemisphere.isRightSideUnlimited = true;
                            lowHemisphere.leftSideLineFactor = lastPerpFactor;
                            lowHemisphere.rightSideLineFactor = 0;

                            highHemisphere.drawHemisphere = true;
                            highHemisphere.isLeftSideUnlimited = isPerpFactorInfinite;
                            highHemisphere.isRightSideUnlimited = true;
                            highHemisphere.leftSideLineFactor = xfactor;
                            highHemisphere.rightSideLineFactor = 0;
                        }
                    }
                    else
                    {
                        if (CompareFactors(xfactor, lastPerpFactor, isPerpFactorInfinite, isLastPerpFactorInfinite) > 0 || (isPerpFactorInfinite && isLastPerpFactorInfinite))
                        {
                            lowHemisphere.drawHemisphere = true;
                            lowHemisphere.isLeftSideUnlimited = true;
                            lowHemisphere.isRightSideUnlimited = isPerpFactorInfinite;
                            lowHemisphere.leftSideLineFactor = 0;
                            lowHemisphere.rightSideLineFactor = xfactor;

                            highHemisphere.drawHemisphere = !isLastPerpFactorInfinite;
                            highHemisphere.isLeftSideUnlimited = true;
                            highHemisphere.isRightSideUnlimited = false;
                            highHemisphere.leftSideLineFactor = 0;
                            highHemisphere.rightSideLineFactor = lastPerpFactor;
                        }
                        else
                        {
                            lowHemisphere.drawHemisphere = true;
                            lowHemisphere.isLeftSideUnlimited = true;
                            lowHemisphere.isRightSideUnlimited = isLastPerpFactorInfinite;
                            lowHemisphere.leftSideLineFactor = 0;
                            lowHemisphere.rightSideLineFactor = lastPerpFactor;

                            highHemisphere.drawHemisphere = !isPerpFactorInfinite;
                            highHemisphere.isLeftSideUnlimited = true;
                            highHemisphere.isRightSideUnlimited = false;
                            highHemisphere.leftSideLineFactor = 0;
                            highHemisphere.rightSideLineFactor = xfactor;
                        }
                    }
                }
                else
                {
                    if (isLastPerpFactorInfinite && isPerpFactorInfinite)
                    {
                        lowHemisphere.drawHemisphere = false;
                        highHemisphere.drawHemisphere = false;
                    }
                    else if (outerForLastPerpIsOnTheLeft)
                    {
                        HemisphereInfo inf1, inf2;
                        if (CompareFactors(xfactor, lastPerpFactor, isPerpFactorInfinite, isLastPerpFactorInfinite) > 0)
                        {
                            inf1 = lowHemisphere;
                            inf2 = highHemisphere;
                        }
                        else
                        {
                            inf1 = highHemisphere;
                            inf2 = lowHemisphere;
                        }
                        inf1.drawHemisphere = true;
                        inf1.isLeftSideUnlimited = isPerpFactorInfinite;
                        inf1.isRightSideUnlimited = isLastPerpFactorInfinite;
                        inf1.leftSideLineFactor = xfactor;
                        inf1.rightSideLineFactor = lastPerpFactor;
                        inf2.drawHemisphere = false;
                    }
                    else
                    {
                        HemisphereInfo inf1, inf2;
                        if (CompareFactors(lastPerpFactor, xfactor, isLastPerpFactorInfinite, isPerpFactorInfinite) > 0)
                        {
                            inf1 = lowHemisphere;
                            inf2 = highHemisphere;
                        }
                        else
                        {
                            inf1 = highHemisphere;
                            inf2 = lowHemisphere;
                        }
                        inf1.drawHemisphere = true;
                        inf1.isLeftSideUnlimited = isLastPerpFactorInfinite;
                        inf1.isRightSideUnlimited = isPerpFactorInfinite;
                        inf1.leftSideLineFactor = lastPerpFactor;
                        inf1.rightSideLineFactor = xfactor;
                        inf2.drawHemisphere = false;
                    }
                }
            }

            if (lowHemisphere.drawHemisphere || highHemisphere.drawHemisphere)
                bresenhamCircle();

            /*unsafe
            {
                byte* pSourceRow = (byte*) bmpData.Scan0.ToPointer();
                pSourceRow += bmpData.Stride * lastMouseXY.Y;
                Int32* pSourcePixel = ((Int32*) pSourceRow) + lastMouseXY.X;
                *((ColorBgra*) pSourcePixel) = ColorBgra.FromColor(Color.Red);
            }

            unsafe
            {
                byte* pSourceRow = (byte*) bmpData.Scan0.ToPointer();
                pSourceRow += bmpData.Stride * y;
                Int32* pSourcePixel = ((Int32*) pSourceRow) + x;
                *((ColorBgra*) pSourcePixel) = ColorBgra.FromColor(Color.Green);
            }*/

            bmp.UnlockBits(bmpData);
            Form.LastModifications.UnlockBits(sourceBmpData);
            Form.BitmapZoomer.InvalidateBitmap(
                Math.Min(x, lastMouseXY.X) - Form.PenWidth / 2 - 1,
                Math.Min(y, lastMouseXY.Y) - Form.PenWidth / 2 - 1,
                Math.Abs(x - lastMouseXY.X) + Form.PenWidth + 2,
                Math.Abs(y - lastMouseXY.Y) + Form.PenWidth + 2);
            lastMouseXY = new Point(x, y);

            lastPerpFactor = xfactor;
            isLastPerpFactorInfinite = isPerpFactorInfinite;
            outerForLastPerpIsOnTheLeft = outerForNextPerpIsOnTheLeft;

        }

        protected abstract unsafe void DrawLine(byte* pSourceRow, byte* pTargetRow, int xs, int xe, int yn, bool circle);

        private int CompareFactors(double C1, double C2, bool isC1Infinite, bool isC2Infinite)
        {
            if (isC1Infinite && isC2Infinite)
                return 0;
            else if (isC1Infinite)
                return -1;
            else if (isC2Infinite)
                return 1;
            else if (C1 == C2)
                return 0;
            else if (C1 < C2)
                return -1;
            else
                return 1;
        }

        private unsafe void drawLineInHemisphere(int x1, int x2, int y, HemisphereInfo info)
        {
            if (!info.drawHemisphere)
                return;

            int yn = y + lastMouseXY.Y;

            if (yn < 0 || yn >= bitmapHeight)
                return;

            int xs, xe;
            if (info.isLeftSideUnlimited)
                xs = x1 + lastMouseXY.X;
            else
                xs = Math.Max(x1, (int) (y * info.leftSideLineFactor)) + lastMouseXY.X;

            if (xs < 0)
                xs = 0;

            if (info.isRightSideUnlimited)
                xe = x2 + lastMouseXY.X;
            else
                xe = Math.Min(x2, (int) (y * info.rightSideLineFactor)) + lastMouseXY.X;

            if (xe >= bitmapWidth)
                xe = bitmapWidth - 1;

            byte* pSourceRow = ((byte*) sourceBmpData.Scan0.ToPointer()) + yn * sourceBmpData.Stride;
            byte* pTargetRow = ((byte*) bmpData.Scan0.ToPointer()) + yn * bmpData.Stride;
            DrawLine(pSourceRow, pTargetRow, xs, xe, yn, true);
        }

        private void bresenhamCircle()
        {
            /*
             * This brilliant algorithm was developed by Jack E. Bresenham, many thanks to him.
             * It is the result of nontrivial algebraic exercises, and I didn't dig into them deeply. But it works :-)
             */
            int x, y, d;
            int r = Form.PenWidth / 2;
            d = 3 - 2 * r;
            x = 0;
            oldBresenhamY = r + 1;
            y = r;
            while (x <= y)
            {
                if (oldBresenhamY != y)
                {
                    drawLineInHemisphere(-x, x, y, highHemisphere);
                    drawLineInHemisphere(-x, x, -y, lowHemisphere);
                    oldBresenhamY = y;
                }
                else
                {
                    drawLineInHemisphere(-x, -x + 1, y, highHemisphere);
                    drawLineInHemisphere(x - 1, x, y, highHemisphere);
                    drawLineInHemisphere(-x, -x + 1, -y, lowHemisphere);
                    drawLineInHemisphere(x - 1, x, -y, lowHemisphere);
                }

                drawLineInHemisphere(-y, y, x, highHemisphere);
                drawLineInHemisphere(-y, y, -x, lowHemisphere);

                if (d < 0)
                    d = d + 4 * x + 6;
                else
                {
                    d = d + 4 * (x - y) + 10;
                    y--;
                }
                x++;
            }
        }

        public override void MouseMove(int x, int y)
        {
            DrawSegment(x, y, true);
        }

        public override void MouseUp()
        {
            DrawSegment(lastMouseXY.X, lastMouseXY.Y, true);
            Form.PushToUndo();
            //coefs = null;
        }
    }

    public class BrushTool: BaseBrushTool
    {
        public BrushTool()
            : base("Select solid brush")
        {
            Name = "Solid brush";
            ParentName = "BrushType";
        }

        public override void SelectTool()
        {
            Form.HintLabel.Text = "Painting with brush";
        }

        protected override unsafe void DrawLine(byte* pSourceRow, byte* pTargetRow, int xs, int xe, int yn, bool circle)
        {
            ColorBgra* pTargetPixel = (ColorBgra*) pTargetRow;
            pTargetPixel += xs;

            for (int xn = xs; xn <= xe; xn++)
            {
                *(pTargetPixel) = selectedColor;
                pTargetPixel++;
            }
        }
    }

/*    public class BrushTool: PaintTool
    {
        int oldx;
        int oldy;
        bool hasOld;

        public BrushTool()
        {
            ParentName = "BrushType";
        }

        public override void SelectTool()
        {
            Form.HintLabel.Text = "Painting with brush";
            hasOld = false;
        }

        public static int HalfMulHalf(int from)
        {
            if (from == 1)
                return 0;
            else if (from == 2)
                return 1;
            else if (from == 3)
                return 3;
            else if (from == 4)
                return 4;
            else if (from == 5)
                return 5;
            else if (from == 7)
                return 10;
            else if (from == 9)
                return 20;
            else
                return from * from / 4;
        }

        void drawOnePoint(Graphics gr, int x, int y)
        {
            Bitmap bmp = Form.BitmapZoomer.Bitmap;
            int sqr = HalfMulHalf(Form.PenWidth);

            Pen pen = new Pen(Form.PaletteSelectedColor);
            for (int x1 = Math.Max(0, x - Form.PenWidth); x1 <= x + Form.PenWidth; x1++)
            {
                if (x1 >= bmp.Width)
                    return;
                int sx = sqr - (x1 - x) * (x1 - x);
                int y1 = y - Form.PenWidth;
                bool nextIter = true;

                while (true)
                {
                    if (y1 > y + Form.PenWidth || y1 >= bmp.Height)
                        break;
                    if ((y1 - y) * (y1 - y) <= sx && y1 >= 0)
                    {
                        nextIter = false;
                        break;
                    }
                    y1 ++;
                }

                if (nextIter)
                    continue;

                int y2 = y1;
                while (true)
                {
                    if ((y2 - y) * (y2 - y) > sx || y2 > y + Form.PenWidth || y2 >= bmp.Height)
                        break;
                    y2 ++;
                }
                if (y1 == y2 - 1)
                    bmp.SetPixel(x1, y1, Form.PaletteSelectedColor);
                else
                    gr.DrawLine(pen, x1, y1, x1, y2 - 1);

            }
        }

        public override void MouseDown(int x, int y)
        {
            Graphics gr = Graphics.FromImage(Form.BitmapZoomer.Bitmap);
            if (hasOld && (oldx - x) * (oldx - x) + (oldy - y) * (oldy - y) > Form.PenWidth * Form.PenWidth / 4)
            {
                drawOnePoint(gr, x, y);
                gr.DrawLine(new Pen(Form.PaletteSelectedColor, Form.PenWidth), oldx, oldy, x, y);
                Form.BitmapZoomer.InvalidateBitmap(
                    Math.Min(oldx, x) - Form.PenWidth - 1,
                    Math.Min(oldy, y) - Form.PenWidth - 1,
                    Math.Abs(oldx - x) + Form.PenWidth + Form.PenWidth + 2,
                    Math.Abs(oldy - y) + Form.PenWidth + Form.PenWidth + 2); //Refresh();
            }
            else
            {
                drawOnePoint(gr, x, y);
                Form.BitmapZoomer.InvalidateBitmap(
                    x - Form.PenWidth - 1,
                    y - Form.PenWidth - 1,
                    Form.PenWidth + Form.PenWidth + 2,
                    Form.PenWidth + Form.PenWidth + 2);
            }

            gr.Dispose();
            oldx = x;
            oldy = y;
            hasOld = true;
        }

        public override void MouseMove(int x, int y)
        {
            MouseDown(x, y);
        }

        public override void MouseUp()
        {
            Form.PushToUndo();
            hasOld = false;
        }

        public override void Cancel()
        {
            hasOld = false;
            base.Cancel();
        }
    }*/

    public class AirBrushTool: BaseBrushTool
    {
        public AirBrushTool()
            : base("Select cloud brush")
        {
            Name = "Cloud brush";

            ParentName = "BrushType";
        }

        public override void SelectTool()
        {
            Form.HintLabel.Text = "Painting with cloud brush";
        }

        protected override unsafe void DrawLine(byte* pSourceRow, byte* pTargetRow, int xs, int xe, int yn, bool circle)
        {
            ColorBgra* pSourcePixel = (ColorBgra*) pSourceRow;
            ColorBgra* pTargetPixel = (ColorBgra*) pTargetRow;
            pSourcePixel += xs;
            pTargetPixel += xs;
            //double a, b = 0;
            int a, b = 0;
            if (circle)
            {
                a = (yn - lastMouseXY.Y) * (yn - lastMouseXY.Y);
            }
            else
            {
                a = (currX * lastMouseXY.Y - lastMouseXY.X * currY + yn * intDX) * 1000;
                b = intDY * 1000;
            }

            for (int xn = xs; xn <= xe; xn++)
            {
                //double h;
                int h;
                if (circle)
                {
                    h = (xn - lastMouseXY.X);
                    h = (h * h + a) * 100;
                }
                else
                {
                    h = (a - xn * b) / intL100;
                    h = h * h;
                }

                h = 100 - h * 4 / Form.PenWidth / Form.PenWidth;

                //h = 100 * (1 - h);
                /*if (h <= 0.04)
                    h = 100 - h*400;
                else if (h <= 0.09)
                    h = 84 - (h - 0.04) * 200;
                else
                    h = 74 - (h - 0.09) * 80;*/

                byte c = (byte) (Math.Max(0, h));
                if (c > coefs[xn, yn])
                {
                    //c = Math.Max(coefs[xn, yn], c);
                    coefs[xn, yn] = c;
                    *(pTargetPixel) = ColorBgra.LerpIntPercent(*pSourcePixel, selectedColor, c);
                }
                //*(pTargetPixel) = ColorBgra.Lerp(*pSourcePixel, selectedColor, 0.2);
                pSourcePixel++;
                pTargetPixel++;
            }
        }
    }

    public class RealAirBrushTool: BaseBrushTool
    {
        Random random = new Random();
        public RealAirBrushTool()
            : base("Select air brush")
        {
            Name = "Air brush";
            ParentName = "BrushType";
        }

        public override void SelectTool()
        {
            Form.HintLabel.Text = "Painting with air brush";
        }

        protected override unsafe void DrawLine(byte* pSourceRow, byte* pTargetRow, int xs, int xe, int yn, bool circle)
        {
            ColorBgra* pSourcePixel = (ColorBgra*) pSourceRow;
            ColorBgra* pTargetPixel = (ColorBgra*) pTargetRow;
            pSourcePixel += xs;
            pTargetPixel += xs;

            for (int xn = xs; xn <= xe; xn++)
            {
                if (random.Next() % 100 < Form.AirBrushDensity)
                    *(pTargetPixel) = selectedColor;
                //*(pTargetPixel) = ColorBgra.Lerp(*pSourcePixel, selectedColor, 0.2);
                pSourcePixel++;
                pTargetPixel++;
            }
        }
    }

    public class SemiTransparentBrushTool: BaseBrushTool
    {
        public SemiTransparentBrushTool()
            : base("Select semitransparent brush")
        {
            Name = "Semitransparent brush";
            ParentName = "BrushType";
        }

        public override void SelectTool()
        {
            Form.HintLabel.Text = "Painting with semitransparent brush";
        }

        protected override unsafe void DrawLine(byte* pSourceRow, byte* pTargetRow, int xs, int xe, int yn, bool circle)
        {
            ColorBgra* pSourcePixel = (ColorBgra*) pSourceRow;
            ColorBgra* pTargetPixel = (ColorBgra*) pTargetRow;
            pSourcePixel += xs;
            pTargetPixel += xs;

            for (int xn = xs; xn <= xe; xn++)
            {
                if (coefs[xn, yn] == 0)
                {
                    coefs[xn, yn] = 1;
                    *(pTargetPixel) = ColorBgra.LerpIntPercent(*pSourcePixel, selectedColor, 100 - Form.SemitransparentBrushTransparency);
                }
                pSourcePixel++;
                pTargetPixel++;
            }
        }
    }

    public class MotionBlurBrushTool: BaseBrushTool
    {
        Point[] pointArray;
        public MotionBlurBrushTool()
            : base("Select motion blur brush")
        {
            Name = "Smearing brush";
            ParentName = "BrushType";
        }

        protected override void ComputeAdditionalData()
        {
            int pn = Form.PenWidth;
            if (pn > l / 2)
                pn = (int) (l / 2);

            pointArray = new Point[pn * 5];
            for (int i = 0; i < pn; i ++)
            {
                int x = (int) (dx * i / l);
                int y = (int) (dy * i / l);
                pointArray[i * 5] = new Point(x, y);
                pointArray[i * 5 + 1] = new Point(-1 + x, y);
                pointArray[i * 5 + 2] = new Point(1 + x, y);
                pointArray[i * 5 + 3] = new Point(x, -1 + y);
                pointArray[i * 5 + 4] = new Point(x, 1 + y);
            }

            base.ComputeAdditionalData();
        }

        protected override unsafe void DrawLine(byte* pSourceRow, byte* pTargetRow, int xs, int xe, int yn, bool circle)
        {
            /*if (circle)
                return;*/

            ColorBgra* pTargetPixel = (ColorBgra*) pTargetRow;
            pTargetPixel += xs;
            /*double a, b = 0;
            a = (currX * lastMouseXY.Y - lastMouseXY.X * currY + yn * dx) / l;
            b = dy / l;*/
            int stride = sourceBmpData.Stride;
            int ic = pointArray.Length;
            if (ic == 0)
                return;

            for (int xn = xs; xn <= xe; xn++)
            {
                /*double h;
                h = a - xn * b;
                h = h * h;

                h = h / Form.PenWidth / Form.PenWidth * 4D;
                int ic = (int) ((1 - h) * pointArray.Length);*/

                int r = 0;
                int g = 0;
                int b = 0;
                int c = 0;
                for (int i = 0; i < ic; i++)
                {
                    Point p = pointArray[i];
                    if (yn + p.Y >= 0 && yn + p.Y < bitmapHeight && xn + p.X >= 0 && xn + p.X < bitmapWidth)
                    {
                        //byte* pSourceRow1 = ((byte*) sourceBmpData.Scan0.ToPointer()) + (yn + p.Y) * sourceBmpData.Stride;
                        ColorBgra* pSourcePixel = (ColorBgra*) (pSourceRow + p.Y * stride);
                        pSourcePixel += (xn + p.X);
                        r += pSourcePixel->R;
                        g += pSourcePixel->G;
                        b += pSourcePixel->B;
                        c++;
                    }
                }

                *(pTargetPixel) = ColorBgra.FromRgb((byte) (r / c), (byte) (g / c), (byte) (b / c));
                pTargetPixel++;
            }
        }
    }

/*    public class AirBrushTool: PaintTool
        {
            int oldx;
            int oldy;
            bool hasOld;

            public void ApplyBrushUnsafe(BitmapData bmpData, Bitmap bmp, int x, int y, int penWidth, Color sColor)
            {
                int sqr = BrushTool.HalfMulHalf(penWidth);

                float s1 = (float) (2 * Math.Sqrt(sqr));

                ColorBgra sBrga = ColorBgra.FromColor(sColor);
                unsafe
                {
                    byte* pSourceRow = (byte*) bmpData.Scan0.ToPointer();

                    int ystart = Math.Max(0, y - penWidth);
                    int yend = Math.Min(bmp.Height - 1, y + penWidth);

                    int xstart = Math.Max(0, x - penWidth);
                    int xend = Math.Min(bmp.Width - 1, x + penWidth);

                    pSourceRow += bmpData.Stride * ystart;
                    Int32* pSourcePixel;

                    for (int y1 = ystart; y1 <= yend; y1++, pSourceRow += bmpData.Stride)
                    {
                        int y2 = (y1 - y) * (y1 - y);
                        pSourcePixel = (Int32*) pSourceRow;
                        pSourcePixel += xstart;
                        for (int x1 = xstart; x1 <= xend; x1++, pSourcePixel++)
                        {
                            int l = (x1 - x) * (x1 - x) + y2;
                            if (l <= sqr)
                            {
                                float l1 = (float) Math.Sqrt(l);
                                *((ColorBgra*) pSourcePixel) = ColorBgra.Lerp(*((ColorBgra*) pSourcePixel), sBrga, (s1 * 1 / 2 + 1 - l1) / (s1 + 1));
                            }
                        }
                    }
                }
            }

            public static void ApplyBrushSafe(Bitmap bmp, int x, int y, int penWidth, Color sColor)
            {
                int sqr = BrushTool.HalfMulHalf(penWidth);

                float s1 = (float) (1.5 * Math.Sqrt(sqr));
                for (int x1 = x - penWidth; x1 <= x + penWidth; x1++)
                {
                    if (x1 < 0 || x1 >= bmp.Width)
                        continue;

                    int x2 = (x1 - x) * (x1 - x);
                    //int sx = sqr - x2;

                    for (int y1 = y - penWidth; y1 <= y + penWidth; y1++)
                    {
                        if (y1 >= 0 && y1 < bmp.Height)
                        {
                            int l = (y1 - y) * (y1 - y) + x2;
                            if (l <= sqr)
                            {
                                float l1 = (float) Math.Sqrt(l);
                                Color oldColor = bmp.GetPixel(x1, y1);
                                Color newColor = Color.FromArgb(
                                    (int) ((oldColor.R * (l1 + s1 / 3) + sColor.R * (s1 * 2 / 3 + 1 - l1)) / (s1 + 1)),
                                    (int) ((oldColor.G * (l1 + s1 / 3) + sColor.G * (s1 * 2 / 3 + 1 - l1)) / (s1 + 1)),
                                    (int) ((oldColor.B * (l1 + s1 / 3) + sColor.B * (s1 * 2 / 3 + 1 - l1)) / (s1 + 1)));
                                bmp.SetPixel(x1, y1, newColor);
                                //Form.BitmapZoomer.SetAt(x1, y1, newColor);
                            }
                        }
                    }
                }
            }

            void drawOnePoint(BitmapData bmpData, int x, int y)
            {
                ApplyBrushUnsafe(bmpData, Form.BitmapZoomer.Bitmap, x, y, Form.PenWidth, Form.PaletteSelectedColor);
            }

            public override void MouseDown(int x, int y)
            {
                Rectangle rect = new Rectangle(0, 0, Form.BitmapZoomer.Bitmap.Width, Form.BitmapZoomer.Bitmap.Height);
                if (hasOld)
                {
                    if ((oldx - x) * (oldx - x) + (oldy - y) * (oldy - y) > Form.PenWidth * Form.PenWidth / 4)
                    {
                        BitmapData bmpData = Form.BitmapZoomer.Bitmap.LockBits(rect, ImageLockMode.ReadWrite, ColorBgra.PixelFormat);
                        float deltaX = x - oldx;
                        float deltaY = y - oldy;
                        float length = (float) Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
                        int newx = oldx, newy = oldy;

                        for (float pos = 0; pos < length; pos += Form.PenWidth / 2)
                        {
                            newx = oldx + Convert.ToInt32(deltaX * pos / length);
                            newy = oldy + Convert.ToInt32(deltaY * pos / length);
                            drawOnePoint(bmpData, newx, newy);
                        }
                        if (newx != x || newy != y)
                        {
                            drawOnePoint(bmpData, newx, newy);
                        }

                        Form.BitmapZoomer.Bitmap.UnlockBits(bmpData);
                        Form.BitmapZoomer.InvalidateBitmap(
                            Math.Min(oldx, x) - Form.PenWidth - 1,
                            Math.Min(oldy, y) - Form.PenWidth - 1,
                            Math.Abs(oldx - x) + Form.PenWidth + Form.PenWidth + 2,
                            Math.Abs(oldy - y) + Form.PenWidth + Form.PenWidth + 2); //Refresh();


                        oldx = x;
                        oldy = y;
                    }
                }
                else
                {
                    BitmapData bmpData = Form.BitmapZoomer.Bitmap.LockBits(rect, ImageLockMode.ReadWrite, ColorBgra.PixelFormat);
                    drawOnePoint(bmpData, x, y);
                    Form.BitmapZoomer.Bitmap.UnlockBits(bmpData);
                    Form.BitmapZoomer.InvalidateBitmap(x - Form.PenWidth, y - Form.PenWidth, 2 * Form.PenWidth + 1, 2 * Form.PenWidth + 1);
                    oldx = x;
                    oldy = y;
                }

                hasOld = true;
            }

            public override void MouseMove(int x, int y)
            {
                MouseDown(x, y);
            }

            public override void MouseUp()
            {
                Form.PushToUndo();
                hasOld = false;
            }

            public override void Cancel()
            {
                hasOld = false;
                base.Cancel();
            }
        }*/

    public class BlurBrushTool: BaseBrushTool
    {
        int r;
        int[] w;
        int wlen;
        Bitmap bmp;

        public BlurBrushTool()
            : base("Select blur brush")
        {
            Name = "Blur brush";
            ParentName = "BrushType";
        }

        public override void SelectTool()
        {
            Form.HintLabel.Text = "Blurring with brush";
        }

        public static int[] CreateGaussianBlurRow(int amount)
        {
            int size = 1 + (amount * 2);
            int[] weights = new int[size];

            for (int i = 0; i <= amount; ++i)
            {
                // 1 + aa - aa + 2ai - ii
                weights[i] = 16 * (i + 1);
                weights[weights.Length - i - 1] = weights[i];
            }

            return weights;
        }

        protected override void ComputeAdditionalData()
        {
            bmp = Form.BitmapZoomer.Bitmap;
            r = Form.PenWidth / 3;
            if (r < 1)
                r = 1;

            w = CreateGaussianBlurRow(r);
            wlen = w.Length;
            base.ComputeAdditionalData();
        }

        protected override unsafe void DrawLine(byte* pSourceRow1, byte* pTargetRow, int xstart, int xend, int y, bool circle)
        {
            byte* pSourceRow = (byte*) sourceBmpData.Scan0.ToPointer();
            int stride = sourceBmpData.Stride;

            long[] waSums = new long[wlen];
            long[] wcSums = new long[wlen];
            long[] aSums = new long[wlen];
            long[] bSums = new long[wlen];
            long[] gSums = new long[wlen];
            long[] rSums = new long[wlen];
            long waSum = 0;
            long wcSum = 0;
            long aSum = 0;
            long bSum = 0;
            long gSum = 0;
            long rSum = 0;
            ColorBgra* pTargetPixel = (ColorBgra*) pTargetRow;
            pTargetPixel += xstart;

            /*ColorBgra* pSourcePixel = (ColorBgra*) pSourceRow;
            pSourcePixel += xs;*/

            for (int wx = 0; wx < wlen; ++wx)
            {
                int srcX = xstart + wx - r;
                waSums[wx] = 0;
                wcSums[wx] = 0;
                aSums[wx] = 0;
                bSums[wx] = 0;
                gSums[wx] = 0;
                rSums[wx] = 0;

                if (srcX >= 0 && srcX < bmp.Width)
                {
                    byte* pB = pSourceRow + srcX * 4;
                    for (int wy = 0; wy < wlen; ++wy)
                    {
                        int srcY = y + wy - r;

                        if (srcY >= 0 && srcY < bmp.Height)
                        {
                            ColorBgra c = *(ColorBgra*) (pB + stride * srcY);
                            int wp = w[wy];

                            waSums[wx] += wp;
#if PocketPC
                            wp *= 255 + (255 >> 7);
#else
                                    wp *= c.A + (c.A >> 7);
#endif
                            wcSums[wx] += wp;
                            wp >>= 8;

                            aSums[wx] += wp * c.A;
                            bSums[wx] += wp * c.B;
                            gSums[wx] += wp * c.G;
                            rSums[wx] += wp * c.R;
                        }
                    }

                    int wwx = w[wx];
                    waSum += wwx * waSums[wx];
                    wcSum += wwx * wcSums[wx];
                    aSum += wwx * aSums[wx];
                    bSum += wwx * bSums[wx];
                    gSum += wwx * gSums[wx];
                    rSum += wwx * rSums[wx];
                }
            }

            wcSum >>= 8;

            if (
#if !PocketPC
                        waSum == 0 || 
#endif
                wcSum == 0)
            {
                *(pTargetPixel) = ColorBgra.FromRgb(0, 0, 0);
            }
            else
            {
                int alpha = (int) (aSum / waSum);
                int blue = (int) (bSum / wcSum);
                int green = (int) (gSum / wcSum);
                int red = (int) (rSum / wcSum);

                *(pTargetPixel) = ColorBgra.FromRgba(Utility.ClampToByte(red), Utility.ClampToByte(green), Utility.ClampToByte(blue), Utility.ClampToByte(alpha));
                //dst[y - ystart][dstPtr] = (int) ColorBgra.BgraToUInt32(blue, green, red, alpha);
            }

            pTargetPixel++;

            for (int x = xstart + 1; x <= xend; ++x)
            {
                for (int i = 0; i < wlen - 1; ++i)
                {
                    waSums[i] = waSums[i + 1];
                    wcSums[i] = wcSums[i + 1];
                    aSums[i] = aSums[i + 1];
                    bSums[i] = bSums[i + 1];
                    gSums[i] = gSums[i + 1];
                    rSums[i] = rSums[i + 1];
                }

                waSum = 0;
                wcSum = 0;
                aSum = 0;
                bSum = 0;
                gSum = 0;
                rSum = 0;

                int wx;
                for (wx = 0; wx < wlen - 1; ++wx)
                {
                    long wwx = (long) w[wx];
                    waSum += wwx * waSums[wx];
                    wcSum += wwx * wcSums[wx];
                    aSum += wwx * aSums[wx];
                    bSum += wwx * bSums[wx];
                    gSum += wwx * gSums[wx];
                    rSum += wwx * rSums[wx];
                }

                wx = wlen - 1;

                waSums[wx] = 0;
                wcSums[wx] = 0;
                aSums[wx] = 0;
                bSums[wx] = 0;
                gSums[wx] = 0;
                rSums[wx] = 0;

                int srcX = x + wx - r;

                if (srcX >= 0 && srcX < bmp.Width)
                {
                    byte* pB = pSourceRow + srcX * 4;
                    for (int wy = 0; wy < wlen; ++wy)
                    {
                        int srcY = y + wy - r;

                        if (srcY >= 0 && srcY < bmp.Height)
                        {
                            ColorBgra c = *(ColorBgra*) (pB + stride * srcY);
                            int wp = w[wy];

                            waSums[wx] += wp;
#if PocketPC
                            wp *= 255 + (255 >> 7);
#else
                                    wp *= c.A + (c.A >> 7);
#endif
                            wcSums[wx] += wp;
                            wp >>= 8;

                            aSums[wx] += wp * (long) c.A;
                            bSums[wx] += wp * (long) c.B;
                            gSums[wx] += wp * (long) c.G;
                            rSums[wx] += wp * (long) c.R;
                        }
                    }

                    int wr = w[wx];
                    waSum += (long) wr * waSums[wx];
                    wcSum += (long) wr * wcSums[wx];
                    aSum += (long) wr * aSums[wx];
                    bSum += (long) wr * bSums[wx];
                    gSum += (long) wr * gSums[wx];
                    rSum += (long) wr * rSums[wx];
                }

                wcSum >>= 8;

                if (
#if !PocketPC
                            waSum == 0 || 
#endif
                    wcSum == 0)
                {
                    //dst[y - ystart][dstPtr] = 0;
                    *(pTargetPixel) = ColorBgra.FromRgb(0, 0, 0);
                }
                else
                {
                    int alpha = (int) (aSum / waSum);
                    int blue = (int) (bSum / wcSum);
                    int green = (int) (gSum / wcSum);
                    int red = (int) (rSum / wcSum);

                    *(pTargetPixel) = ColorBgra.FromRgba(Utility.ClampToByte(red), Utility.ClampToByte(green), Utility.ClampToByte(blue), Utility.ClampToByte(alpha));
                    //dst[y - ystart][dstPtr] = (int) ColorBgra.BgraToUInt32(blue, green, red, alpha);
                }

                pTargetPixel++;
            }
        }
    }

    /*public class BlurBrushTool: PaintTool
    {
        int oldx;
        int oldy;
        bool hasOld;

        public BlurBrushTool()
        {
            Name = "Blur brush";
            ParentName = "BrushType";
        }

        public override void SelectTool()
        {
            Form.HintLabel.Text = "Blurring with brush";
            hasOld = false;
        }

        public static int[] CreateGaussianBlurRow(int amount)
        {
            int size = 1 + (amount * 2);
            int[] weights = new int[size];

            for (int i = 0; i <= amount; ++i)
            {
                // 1 + aa - aa + 2ai - ii
                weights[i] = 16 * (i + 1);
                weights[weights.Length - i - 1] = weights[i];
            }

            return weights;
        }

        public static unsafe void ApplyBrushUnsafe(Bitmap bmp, int xc, int yc, int penWidth, Color sColor)
        {
            Rectangle rect1 = new Rectangle(0, 0, bmp.Width, bmp.Height);
            BitmapData bmpData = bmp.LockBits(rect1, ImageLockMode.ReadWrite, ColorBgra.PixelFormat);
            int r = penWidth/3;
            if (r < 1)
                r = 1;

            //int r = 3;
            int[] w = CreateGaussianBlurRow(r);
            int wlen = w.Length;

            unchecked
            {
                byte* pSourceRow = (byte*) bmpData.Scan0.ToPointer();
                int stride = bmpData.Stride;

                int ystart = Math.Max(0, yc - penWidth);
                int yend = Math.Min(bmp.Height - 1, yc + penWidth);

                int xstart = Math.Max(0, xc - penWidth);
                int xend = Math.Min(bmp.Width - 1, xc + penWidth);

                int[][] dst = new int[yend - ystart + 1][];
                int dstPtr;

                for (int y = ystart; y <= yend; y++)
                {
                    dst[y - ystart] = new int[xend - xstart + 1];

                    long[] waSums = new long[wlen];
                    long[] wcSums = new long[wlen];
                    long[] aSums = new long[wlen];
                    long[] bSums = new long[wlen];
                    long[] gSums = new long[wlen];
                    long[] rSums = new long[wlen];
                    long waSum = 0;
                    long wcSum = 0;
                    long aSum = 0;
                    long bSum = 0;
                    long gSum = 0;
                    long rSum = 0;
                    dstPtr = 0;

                    for (int wx = 0; wx < wlen; ++wx)
                    {
                        int srcX = xstart + wx - r;
                        waSums[wx] = 0;
                        wcSums[wx] = 0;
                        aSums[wx] = 0;
                        bSums[wx] = 0;
                        gSums[wx] = 0;
                        rSums[wx] = 0;

                        if (srcX >= 0 && srcX < bmp.Width)
                        {
                            byte* pB = pSourceRow + srcX * 4;
                            for (int wy = 0; wy < wlen; ++wy)
                            {
                                int srcY = y + wy - r;

                                if (srcY >= 0 && srcY < bmp.Height)
                                {
                                    ColorBgra c = *(ColorBgra*) (pB + stride * srcY);
                                    int wp = w[wy];

                                    waSums[wx] += wp;
#if PocketPC
                                    wp *= 255 + (255 >> 7);
#else
                                    wp *= c.A + (c.A >> 7);
#endif
                                    wcSums[wx] += wp;
                                    wp >>= 8;

                                    aSums[wx] += wp * c.A;
                                    bSums[wx] += wp * c.B;
                                    gSums[wx] += wp * c.G;
                                    rSums[wx] += wp * c.R;
                                }
                            }

                            int wwx = w[wx];
                            waSum += wwx * waSums[wx];
                            wcSum += wwx * wcSums[wx];
                            aSum += wwx * aSums[wx];
                            bSum += wwx * bSums[wx];
                            gSum += wwx * gSums[wx];
                            rSum += wwx * rSums[wx];
                        }
                    }

                    wcSum >>= 8;

                    if (
#if !PocketPC
                        waSum == 0 || 
#endif
                        wcSum == 0)
                    {
                        dst[y - ystart][dstPtr] = 0;
                    }
                    else
                    {
                        int alpha = (int) (aSum / waSum);
                        int blue = (int) (bSum / wcSum);
                        int green = (int) (gSum / wcSum);
                        int red = (int) (rSum / wcSum);

                        dst[y - ystart][dstPtr] = (int) ColorBgra.BgraToUInt32(blue, green, red, alpha);
                    }

                    ++dstPtr;

                    for (int x = xstart + 1; x <= xend; ++x)
                    {
                        for (int i = 0; i < wlen - 1; ++i)
                        {
                            waSums[i] = waSums[i + 1];
                            wcSums[i] = wcSums[i + 1];
                            aSums[i] = aSums[i + 1];
                            bSums[i] = bSums[i + 1];
                            gSums[i] = gSums[i + 1];
                            rSums[i] = rSums[i + 1];
                        }

                        waSum = 0;
                        wcSum = 0;
                        aSum = 0;
                        bSum = 0;
                        gSum = 0;
                        rSum = 0;

                        int wx;
                        for (wx = 0; wx < wlen - 1; ++wx)
                        {
                            long wwx = (long) w[wx];
                            waSum += wwx * waSums[wx];
                            wcSum += wwx * wcSums[wx];
                            aSum += wwx * aSums[wx];
                            bSum += wwx * bSums[wx];
                            gSum += wwx * gSums[wx];
                            rSum += wwx * rSums[wx];
                        }

                        wx = wlen - 1;

                        waSums[wx] = 0;
                        wcSums[wx] = 0;
                        aSums[wx] = 0;
                        bSums[wx] = 0;
                        gSums[wx] = 0;
                        rSums[wx] = 0;

                        int srcX = x + wx - r;

                        if (srcX >= 0 && srcX < bmp.Width)
                        {
                            byte* pB = pSourceRow + srcX * 4;
                            for (int wy = 0; wy < wlen; ++wy)
                            {
                                int srcY = y + wy - r;

                                if (srcY >= 0 && srcY < bmp.Height)
                                {
                                    ColorBgra c = *(ColorBgra*) (pB + stride * srcY);
                                    int wp = w[wy];

                                    waSums[wx] += wp;
#if PocketPC
                                    wp *= 255 + (255 >> 7);
#else
                                    wp *= c.A + (c.A >> 7);
#endif
                                    wcSums[wx] += wp;
                                    wp >>= 8;

                                    aSums[wx] += wp * (long) c.A;
                                    bSums[wx] += wp * (long) c.B;
                                    gSums[wx] += wp * (long) c.G;
                                    rSums[wx] += wp * (long) c.R;
                                }
                            }

                            int wr = w[wx];
                            waSum += (long) wr * waSums[wx];
                            wcSum += (long) wr * wcSums[wx];
                            aSum += (long) wr * aSums[wx];
                            bSum += (long) wr * bSums[wx];
                            gSum += (long) wr * gSums[wx];
                            rSum += (long) wr * rSums[wx];
                        }

                        wcSum >>= 8;

                        if (
#if !PocketPC
                            waSum == 0 || 
#endif
                            wcSum == 0)
                        {
                            dst[y - ystart][dstPtr] = 0;
                        }
                        else
                        {
                            int alpha = (int) (aSum / waSum);
                            int blue = (int) (bSum / wcSum);
                            int green = (int) (gSum / wcSum);
                            int red = (int) (rSum / wcSum);

                            dst[y - ystart][dstPtr] = (int) ColorBgra.BgraToUInt32(blue, green, red, alpha);
                        }

                        ++dstPtr;
                    }
                }

                pSourceRow += 4 * xstart + ystart * stride;
                for (int y = ystart; y <= yend; y++, pSourceRow += stride)
                {
                    System.Runtime.InteropServices.Marshal.Copy(dst[y - ystart], 0, new IntPtr(pSourceRow), xend - xstart + 1);
                }
            }
            bmp.UnlockBits(bmpData);
        }

        void drawOnePoint(int x, int y)
        {
            ApplyBrushUnsafe(Form.BitmapZoomer.Bitmap, x, y, Form.PenWidth / 2 + 1, Form.PaletteSelectedColor);
            Form.BitmapZoomer.InvalidateBitmap(x - Form.PenWidth / 2 - 2, y - Form.PenWidth / 2 - 2, Form.PenWidth + 4, Form.PenWidth + 4);
            *Bitmap bmp = Form.BitmapZoomer.Bitmap;
            int sqr = BrushTool.HalfMulHalf(Form.PenWidth);

            float s1 = Math.Sqrt(sqr);
            Color sColor = Form.PaletteSelectedColor;
            Bitmap bmp1 = new Bitmap(Form.PenWidth * 2 + 1, Form.PenWidth * 2 + 1, PixelFormat.Format32bppRgb);
            for (int x1 = x - Form.PenWidth; x1 <= x + Form.PenWidth; x1++)
                for (int y1 = y - Form.PenWidth; y1 <= y + Form.PenWidth; y1++)
                {
                    if (x1 >= 0 && x1 < bmp.Width && y1 >= 0 && y1 < bmp.Height)
                    {
                        int l = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y);
                        if (l <= sqr)
                        {
                            int r = 0;
                            int g = 0;
                            int b = 0;
                            int c = 0;

                            foreach (Point p in FillTool.Shifts)
                            {
                                int x2 = x1 + p.X;
                                int y2 = y1 + p.Y;
                                if (x2 >= 0 && x2 < bmp.Width && y2 >= 0 && y2 < bmp.Height)
                                {
                                    Color oldColor = Form.BitmapZoomer.Bitmap.GetPixel(x2, y2);
                                    r += oldColor.R;
                                    g += oldColor.G;
                                    b += oldColor.B;
                                    c++;
                                }
                            }

                            bmp1.SetPixel(x1 - x + Form.PenWidth, y1 - y + Form.PenWidth, Color.FromArgb(r/c, g/c, b/c));
                        }
                        else
                        {
                            Color oldColor = Form.BitmapZoomer.Bitmap.GetPixel(x1, y1);
                            bmp1.SetPixel(x1 - x + Form.PenWidth, y1 - y + Form.PenWidth, oldColor);
                        }
                    }
                }

            Graphics gr = Graphics.FromImage(Form.BitmapZoomer.Bitmap);
            gr.DrawImage(bmp1, x - Form.PenWidth, y - Form.PenWidth); // wrong
            gr.Dispose();
            bmp1.Dispose();
            //Form.BitmapZoomer.Refresh();*
        }

        public override void MouseDown(int x, int y)
        {
            if ((!hasOld) || x != oldx || y != oldy)
                drawOnePoint(x, y);

            oldx = x;
            oldy = y;
            hasOld = true;
            *if (hasOld)
            {
                if ((oldx - x) * (oldx - x) + (oldy - y) * (oldy - y) > Form.PenWidth * Form.PenWidth / 4)
                {
                    float deltaX = x - oldx;
                    float deltaY = y - oldy;
                    float length = Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
                    int newx = oldx, newy = oldy;

                    for (float pos = 0; pos < length; pos += Form.PenWidth / 2)
                    {
                        newx = oldx + Convert.ToInt32(deltaX * pos / length);
                        newy = oldy + Convert.ToInt32(deltaY * pos / length);
                        drawOnePoint(newx, newy);
                    }
                    if (newx != x || newy != y)
                    {
                        drawOnePoint(newx, newy);
                    }

                    Form.BitmapZoomer.InvalidateBitmap(
                        Math.Min(oldx, x) - Form.PenWidth - 1,
                        Math.Min(oldy, y) - Form.PenWidth - 1,
                        Math.Abs(oldx - x) + Form.PenWidth + Form.PenWidth + 2,
                        Math.Abs(oldy - y) + Form.PenWidth + Form.PenWidth + 2); //Refresh();


                    oldx = x;
                    oldy = y;
                }
            }
            else
            {
                drawOnePoint(x, y);
                Form.BitmapZoomer.InvalidateBitmap(x - Form.PenWidth, y - Form.PenWidth, 2 * Form.PenWidth + 1, 2 * Form.PenWidth + 1);
                oldx = x;
                oldy = y;
            }

            hasOld = true;*
        }

        public override void MouseMove(int x, int y)
        {
            MouseDown(x, y);
        }

        public override void MouseUp()
        {
            Form.PushToUndo();
            hasOld = false;
        }

        public override void Cancel()
        {
            hasOld = false;
            base.Cancel();
        }
    }*/
}